﻿using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices;

namespace deFramework.Common
{
    /// <summary>
    /// 
    /// </summary>
    public class ActiveDirectoryTools
    {


        #region "Active Directory Reusable Code"

        #region "Public - MinimumPasswordLength, DebugStatusMessage"

        /// <summary>
        /// 
        /// </summary>
        public int MinimumPasswordLength = 4;
        
        /// <summary>
        /// 
        /// </summary>
        public string DebugStatusMessage = String.Empty;

        #endregion

        #region "Public - SelfServePasswordChange, AdminPasswordChange, AutoCreateSiacActiveDirectoryUser"

        /// <summary>
        /// 
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="oldPassword"></param>
        /// <param name="newPassword"></param>
        /// <param name="confirmPassword"></param>
        /// <param name="domainName"></param>
        /// <returns></returns>
        public bool SelfServePasswordChange(string userName, string oldPassword, string newPassword, string confirmPassword, string domainName)
        {
            bool _result = false;
            string debugStatus = String.Empty;
            try
            {
                if (this.UserAuthenticates(userName, oldPassword, domainName))
                {
                    _result = this.AdminPasswordChange(userName, newPassword, confirmPassword, domainName);
                    debugStatus = "SelfServePasswordChange; " + this.DebugStatusMessage; 
                }
                else
                {
                    debugStatus = "User " + userName + " failed to successfully login to the " + domainName + " domain with the entered old password.";
                }
            }
            catch (Exception ex)
            {
                debugStatus += ";  Exception:" + ex.Message;
            }
            this.DebugStatusMessage = debugStatus;
            return _result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="newPassword"></param>
        /// <param name="confirmPassword"></param>
        /// <param name="domainName"></param>
        /// <returns></returns>
        public bool AdminPasswordChange(string userName, string newPassword, string confirmPassword, string domainName)
        {
            bool _result = false;
            string debugStatus = String.Empty;
            debugStatus = "AdminPasswordChange: Method Started.";
            this.DebugStatusMessage = debugStatus;
            try
            {
                string validInfo = this.ValidNewPassword(newPassword, confirmPassword);
                 if (validInfo.Length == 0)
                {
                    debugStatus = "AdminPasswordChange: New Password is Valid.";
                    this.DebugStatusMessage = debugStatus;
                    DirectoryEntry deUser = this.UserActiveDirectoryEntry(domainName, userName);
                    debugStatus = "AdminPasswordChange: User found in Active Directory.";
                    this.DebugStatusMessage = debugStatus;
                    string resetMessage = this.ResetUserPassword(ref deUser, newPassword);
                    debugStatus += "; " + resetMessage; 
                    this.DebugStatusMessage = debugStatus;
                    deUser.Close();
                    debugStatus += "; User Object Closed.";
                    this.DebugStatusMessage = debugStatus;
                    _result = true;
                }
                else
                {
                    debugStatus = debugStatus + "; " + validInfo;
                    this.DebugStatusMessage = debugStatus;
                }
            }
            catch (Exception ex)
            {
                if (debugStatus.Length > 0)
                    debugStatus += ";  ";
                debugStatus += "Exception:" + ex.Message;
            }
            this.DebugStatusMessage = debugStatus;
            return _result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="domainName"></param>
        /// <param name="newUserParentNodeDN"></param>
        /// <param name="newUserName"></param>
        /// <param name="newFirst"></param>
        /// <param name="newLast"></param>
        /// <param name="newEmail"></param>
        /// <param name="newPassword"></param>
        /// <returns></returns>
        public bool AutoCreateSiacActiveDirectoryUser(
            string domainName, string newUserParentNodeDN,
            string newUserName, string newFirst, string newLast, string newEmail, string newPassword
            )
        {
            bool _result = false;
            string debugStatus = String.Empty;
            try
            {
                string ldapPath = "LDAP://" + this.FriendlyDomainToLdapDomain(domainName);
                DirectoryEntry deGroup = this.RetrieveTemplateGroup(ref debugStatus, ldapPath, newUserParentNodeDN);
                debugStatus = "group:" + deGroup.Name;
                DirectoryEntry deNew = deGroup.Children.Add("CN=" + newUserName, "user");
                string newNameDN = deNew.Name;
                debugStatus += "; newnameDN:" + deNew.Name;

                debugStatus = this.SetAdUserAttrib(debugStatus, ref deNew, "samAccountName", newUserName);
                this.ResetUserPassword(ref deNew, newPassword);
                deNew.Invoke("SetPassword", new object[] { newPassword });
                debugStatus += "; Password";
                deNew.CommitChanges();

                debugStatus = this.SetAdUserAttrib(debugStatus, ref deNew, "givenName", newFirst);
                debugStatus = this.SetAdUserAttrib(debugStatus, ref deNew, "sn", newLast);
                debugStatus = this.SetAdUserAttrib(debugStatus, ref deNew, "displayName", newFirst + " " + newLast);
                debugStatus = this.SetAdUserAttrib(debugStatus, ref deNew, "mail", newEmail);

                _result = this.EnableUserAccount(ref deNew);
            }
            catch (Exception ex)
            {
                debugStatus += ";  Exception:" + ex.Message;
            }
            this.DebugStatusMessage = debugStatus;
            return _result;
        }



        #endregion

        #region "private - UserAuthenticates, ValidNewPassword, ResetUserPassword, UserActiveDirectoryEntry"

        private bool UserAuthenticates(string userName, string password, string domainName)
        {
            bool _result = false;
            try
            {
                string ldapPath = "LDAP://" + this.FriendlyDomainToLdapDomain(domainName);
                DirectoryEntry entry = new DirectoryEntry(ldapPath, userName, password);
                object nativeObject = entry.NativeObject;
                _result = true;
            }
            catch (DirectoryServicesCOMException) { }
            return _result;
        }

        private string ValidNewPassword(string newPassword, string confirmPassword)
        {
            string _result = String.Empty;
            if (newPassword.Length >= this.MinimumPasswordLength)
            {
                if (newPassword != confirmPassword)
                    _result = "Invalid. Passwords do not match.";
            }
            else
                _result = "Invalid. Password length must be " + this.MinimumPasswordLength.ToString() + " or more characters.";
            return _result;
        }


        private string ResetUserPassword(ref DirectoryEntry deUser, string newPassword)
        {
            string _message = "ResetUserPassword: Start.";
            try
            {
                deUser.Invoke("SetPassword", new object[] { newPassword });
                _message = "ResetUserPassword: Set Password."; 
                deUser.Properties["LockOutTime"].Value = 0; //unlock account
                _message = "ResetUserPassword: Set Password. Unlock.";
                deUser.CommitChanges();
                _message = "ResetUserPassword: Set Password. Unlock. CommitChanges.";
            }
            catch (Exception ex)
            {
                _message += "Exception: " + ex.Message; 
            }
            return _message; 
        }


        private DirectoryEntry UserActiveDirectoryEntry(string domainName, string userName)
        {
            DirectoryEntry _result = null;
            try
            {
                DirectoryEntry mySearchRoot = new DirectoryEntry("LDAP://" + this.FriendlyDomainToLdapDomain(domainName));
                DirectorySearcher search = new DirectorySearcher(mySearchRoot);
                search.Filter = String.Format("(&(SAMAccountName={0})) ", userName);
                _result = search.FindOne().GetDirectoryEntry();
            }
            catch  {} //(Exception ex)
            return _result;
        }


        #endregion

        #region "private - FriendlyDomainToLdapDomain, RetrieveTemplateGroup, RetrieveSubfolder"

      
        private string FriendlyDomainToLdapDomain(string friendlyDomainName)
        {
            string ldapPath = "NONE";
            try
            {
                System.DirectoryServices.ActiveDirectory.DirectoryContext objContext = new System.DirectoryServices.ActiveDirectory.DirectoryContext(
                    System.DirectoryServices.ActiveDirectory.DirectoryContextType.Domain, friendlyDomainName);
                System.DirectoryServices.ActiveDirectory.Domain objDomain =
                    System.DirectoryServices.ActiveDirectory.Domain.GetDomain(objContext);
                ldapPath = objDomain.Name;
            }
            catch (Exception e)
            {
                ldapPath = e.Message.ToString();
            }
            return ldapPath;
        }


        private DirectoryEntry RetrieveTemplateGroup(ref string statusMessage, string ldapPath, string orgDN)
        {
            DirectoryEntry result = null;
            try
            {
                DirectoryEntry dirRoot = new DirectoryEntry(ldapPath);
                foreach (DirectoryEntry dirChild in dirRoot.Children)
                {
                    try
                    {
                        string cname = dirChild.Name;
                        statusMessage += "; folder=" + cname;
                        if (cname.Trim().ToUpper() == orgDN.Trim().ToUpper())
                        {
                            result = dirChild;
                            statusMessage += "((match))";
                            break;
                        }
                    }
                    catch { }
                }
            }
            catch { }
            return result;
        }




        private DirectoryEntry RetrieveSubfolder(ref string statusMessage, DirectoryEntry dirParent, string subDN)
        {
            DirectoryEntry result = null;
            try
            {
                foreach (DirectoryEntry dirChild in dirParent.Children)
                {
                    try
                    {
                        string cname = dirChild.Name;
                        statusMessage += "; subname=" + cname;
                        if (cname.Trim().ToUpper().Contains(subDN.Trim().ToUpper()))
                        {
                            result = dirChild;
                            statusMessage += "((submatch))";
                            break;
                        }
                    }
                    catch { }
                }
            }
            catch { }
            return result;
        }






        #endregion

        #region "private - SetAdUserAttrib, EnableUserAccount, AddUserObjectToGroup"


        private string SetAdUserAttrib(string statusMessage, ref DirectoryEntry deNew, string attribName, string value)
        {
            string result = statusMessage;
            try
            {
                deNew.Properties[attribName].Value = value;
                result += "; OK " + attribName;
                deNew.CommitChanges();
            }
            catch (Exception ex)
            {
                result += "; FAIL " + attribName + "; Exception:" + ex.Message;
            }
            return result;

        }


        private bool EnableUserAccount(ref DirectoryEntry user)
        {
            bool _result = false;
            try
            {
                int val = (int)user.Properties["userAccountControl"].Value;
                user.Properties["userAccountControl"].Value = val & ~0x2;
                //ADS_UF_NORMAL_ACCOUNT;

                user.CommitChanges();
                _result = true;
            }
            catch { }
            //(System.DirectoryServices.DirectoryServicesCOMException ex)
            //{
            //    //DoSomethingWith --> E.Message.ToString();

            //}
            return _result;
        }

        /// <summary>
        /// NOT TESTED TO WORK
        /// </summary>
        /// <param name="deUser"></param>
        /// <param name="ldapPath"></param>
        /// <param name="parentDN"></param>
        /// <param name="groupDN"></param>
        /// <returns></returns>
        private string AddUserObjectToGroup(ref DirectoryEntry deUser, string ldapPath, string parentDN, string groupDN)
        {
            string statusMessage = String.Empty;
            try
            {
                DirectoryEntry deUsersFolder = this.RetrieveTemplateGroup(ref statusMessage, ldapPath, parentDN);
                DirectoryEntry deGroupNode = this.RetrieveSubfolder(ref statusMessage, deUsersFolder, groupDN);
                statusMessage += "; group node";
                string deGroupPath = deGroupNode.Path.ToString();
                statusMessage += "; group path:" + deGroupPath;
                string deNewPath = deUser.Path.ToString();
                statusMessage += "; user path:" + deNewPath;
                string groupMemberProperties = deGroupNode.Properties["member"].ToString();
                statusMessage += "; group members:" + groupMemberProperties;
                deGroupNode.Properties["member"].Add(deNewPath);
                statusMessage += "; group membeer Add for (" + deNewPath + ")";
                deGroupNode.CommitChanges();
                deGroupNode.Close();
                statusMessage += "; group close";
            }
            catch (Exception ex)
            {
                statusMessage += "; Exception:" + ex.Message.ToString();
            }
            return statusMessage;
        }



        #endregion

        #endregion


        #region "Obsolete" 

        /// <summary>
        /// 
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="oldPassword"></param>
        /// <param name="newPassword"></param>
        /// <param name="confirmPassword"></param>
        /// <param name="domainName"></param>
        /// <returns></returns>
        private bool Obsolete_SelfServePasswordChange(string userName, string oldPassword, string newPassword, string confirmPassword, string domainName)
        {
            bool _result = false;
            string debugStatus = String.Empty;
            try
            {
                if (this.UserAuthenticates(userName, oldPassword, domainName))
                {
                    debugStatus = "Error Code A";
                    this.DebugStatusMessage = debugStatus;
                    string validInfo = this.ValidNewPassword(newPassword, confirmPassword);
                    debugStatus = debugStatus + "; valid: " + validInfo;
                    this.DebugStatusMessage = debugStatus;

                    if (validInfo.Length == 0)
                    {
                        DirectoryEntry deUser = this.UserActiveDirectoryEntry(domainName, userName);
                        debugStatus = "Obsolete User found in Active Directory. " + debugStatus;
                        this.DebugStatusMessage = debugStatus;
                        try
                        {
                            this.ResetUserPassword(ref deUser, newPassword);
                        }
                        catch (Exception ex)
                        {
                            debugStatus = debugStatus + "; ResetUserPassword:" + ex.Message;
                        }
                        deUser.Close();
                        _result = true;
                    }
                    else
                    {
                        debugStatus = debugStatus + "; " + validInfo;
                        this.DebugStatusMessage = debugStatus;
                    }
                    //_result = this.AdminPasswordChange(userName, newPassword, confirmPassword, domainName);
                }
                else
                {
                    debugStatus = "User " + userName + " failed to successfully login to the " + domainName + " domain with the entered old password.";
                }
            }
            catch (Exception ex)
            {
                debugStatus += ";  Exception:" + ex.Message;
            }
            this.DebugStatusMessage = debugStatus;
            return _result;
        }

        #endregion 



    }
}
